feat(db): accept a read replica ID in DB operation commands#177
Conversation
The db connection commands (connection-string, connect/psql, test-connection, schema) and the db_execute_query / db_schema MCP tools now resolve a read replica set ID as well as a service ID: it's tried as a service first, then matched against the project's read replica sets. A replica ID connects to that replica's endpoint while credentials resolve against the parent service (replicas share the primary's credentials). When --pooled is requested for a replica that has no pooler, all surfaces warn and fall back to a direct connection. Service-management commands still treat a replica ID as not found. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read replica ID resolution now: - validates a matched replica is active and has an endpoint, returning a clear "not active" / "no endpoint" error instead of failing late at connect time. A new ErrReplicaNotFound sentinel distinguishes a real no-match (fall back to the service error) from a matched-but-unusable replica (surface it). - only scans the project's services when the service lookup failed with a 404; other failures (auth, network, 5xx) are surfaced as-is via the new common.IsNotFound helper, avoiding a wasted service-list call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… scan
The API resolves a read replica ID through the normal service-get endpoint,
returning the replica's own endpoint plus a forked_from link to its parent
(is_standby, service_id). Use that instead of scanning the project's services:
- common.ResolveConnectionTarget turns a fetched service into a
ConnectionTarget{Connect, Credential, IsReplica}. A standby replica connects
to its own endpoint but resolves credentials (and password reset) against the
parent primary, fixing the "password authentication failed" error when
connecting to a replica by ID.
- Removes the ResolveServiceOrReplica/FindReplicaByID list-scan machinery, the
IsNotFound helper, GetReplicaConnectionDetails, ConnectToService/ConnectToTarget.
- Consolidates the pooler policy: ConnectionTarget.Details owns the
build-and-RequirePooler rule; ReplicaPoolerWarning owns the replica guard;
resolveConnectTarget handles both the by-ID replica and the interactive menu.
- getServiceDetails delegates to the shared common.GetService.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the redundant "in place of the service ID" and doubled "read replica" wording from the db connection commands' help and the README, and remove the read-only/lag aside from `db connect` help (kept in the MCP tool description). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
a4104cd to
57e8469
Compare
Askir
left a comment
There was a problem hiding this comment.
Naming confused me a lot here but I think otherwise its good.
What happens if you use update-password, save-password and tiger db create role with a read replica? For the record I think its fine if update-password and create role fail (its a read replica after all so read only) but it should probably have a good error. For save-password I genuinely am not sure if it should override the parents password or error out.
| } | ||
|
|
||
| service, err := getServiceDetailsFunc(cmd, cfg, args) | ||
| target, err := resolveConnectionTarget(cmd, cfg, args) |
There was a problem hiding this comment.
We have resolveConnectionTaget and resolveConnectTarget now doing wildly different things. But reading as if someone typo'd.
Maybe we could clarify these two? One is the connection chooser and returns ConnectionDetails as the result, the other one is the lookup of the ID
| type ConnectionTarget struct { | ||
| Connect api.Service | ||
| Credential api.Service | ||
| IsReplica bool | ||
| } |
There was a problem hiding this comment.
I find this poorly named, it took me a long time while reading to understand that Credential is only the CredentialService/ the service where we resolve the credentials from rather than the actual credentials.
I think it would make sense to at least call the two ConnectionService and CredentialService. Sorry for being a bit pedantic about naming, here. I try not to do that usually but I really struggled understanding what's happening here 😅
…ommands - Build the connection string via net/url so a role or password containing URL-special characters (e.g. a manually entered password) no longer produces an unparseable "invalid userinfo" string. - service update-password (CLI + MCP) and db create role reject a read replica ID, pointing at the primary — a read-only replica can't be written to, and replicas share the primary's credentials. Adds common.IsReadReplica. - db save-password redirects a replica ID to the parent primary, storing the password where the connect commands read it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename ConnectionTarget.Connect -> ConnectionService and Credential -> CredentialService so the field names convey that Credential is the service whose credentials are used, not the credentials themselves. Trim the now- redundant doc comments the self-documenting names replace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per review feedback, the near-identical resolveConnectionTarget / resolveConnectTarget read like a typo. Rename the args->ConnectionTarget lookup to lookupConnectionTarget and the interactive chooser (->ConnectionDetails) to selectConnection, and update their tests. common.ResolveConnectionTarget keeps its name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
That was exactly the changes I was still working on (94aadd7), For |
54718de to
f61c856
Compare
Condense the overlong CLAUDE.md read-replica bullet to a short pointer and fix the stale ConnectionTarget field names (ConnectionService/CredentialService). Drop the forked_from/service-get implementation detail from spec.md, keeping the user-facing behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
f61c856 to
ec57555
Compare
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The tiger db connection commands (connection-string, connect/psql, test-connection, schema) and the MCP db_execute_query / db_schema tools now accept a read replica set ID wherever a service ID is accepted. A replica ID connects to that replica's endpoint using the parent primary's credentials (replicas share them); a primary ID behaves as before.
The API resolves a replica ID via the normal service-get endpoint, returning the replica's endpoint plus a forked_from link to its parent — so credentials (and password reset) resolve against the parent.